// -*- C++ -*-

#include <sstream>
#include <iostream>
#include <stdexcept>
#include <boost/shared_ptr.hpp>
#include <rtm/idl/BasicDataTypeSkel.h>
#include <coil/Properties.h>
#include <services/StateService/StateService.hh>

#ifndef STATE_H
#define STATE_H

class EInvalidState : public std::runtime_error
{
public:
    EInvalidState(const std::string &name) : std::runtime_error( "No such state: " + name ) {}
};

class EInvalidStateType : public std::runtime_error
{
public:
    EInvalidStateType(const std::string &name) : std::runtime_error( "No such StateType: "+ name) {}
};

class EInvalidStateDataname : public std::runtime_error
{
public:
    EInvalidStateDataname(const std::string &dname) : std::runtime_error( "No such dataname: " + dname ) {}
};

class EBindError : public std::runtime_error
{
public:
    EBindError(const std::string &name) : std::runtime_error( "Bind failed: " + name ) {}
};

//StateProfileから読み込んだときにStateProfileのデータ型から適切なデータ列に変換するためのクラス(Verboseなど)
class StateElementFuncBase;
typedef boost::shared_ptr<StateElementFuncBase> StateElementFuncPtr;
template<typename T>  class StateElementFunc;
class StateElementFuncBase
{
public:
    static StateElementFuncPtr create(RTMEUS::StateType type);
    StateElementFuncBase(const std::string &Sprefix) : m_Sprefix(Sprefix){}
    virtual ~StateElementFuncBase() {}
    virtual void load(std::istream& is, void *ptr) const = 0;
    virtual void store(std::ostream& os, void *ptr) const = 0;
    virtual double toDouble(void *ptr) const = 0;
    virtual void copy(void *dst, void *src) const = 0;
    std::string getSprefix() const {return m_Sprefix;}
protected:
    std::string m_Sprefix;
};

template<typename T>
class StateElementFunc
    :public StateElementFuncBase
{
public:
    StateElementFunc(const std::string &Sprefix) : StateElementFuncBase(Sprefix){}
    virtual ~StateElementFunc() {}
    virtual void load(std::istream& is, void *ptr) const {is >> *(T*)ptr;}
    virtual void store(std::ostream& os, void *ptr) const {os << *(T*)ptr;}
    virtual double toDouble(void *ptr) const {return (double)(*(T*)ptr);}
    virtual void copy(void *dst, void *src) const {*(T*)dst = *(T*)src;}
};

template<>
class StateElementFunc<unsigned char>
    :public StateElementFuncBase
{
public:
    StateElementFunc(const std::string &Sprefix) : StateElementFuncBase(Sprefix){}
    virtual ~StateElementFunc() {}
    virtual void load(std::istream& is, void *ptr) const {short tmp; is >> tmp; *(unsigned char*)ptr = (unsigned char)tmp;}
    virtual void store(std::ostream& os, void *ptr) const {os << std::hex
                                                              << (short)(*(unsigned char*)ptr)
                                                              << std::dec;}
    virtual double toDouble(void *ptr) const {return (double)(*(unsigned char*)ptr);}
    virtual void copy(void *dst, void *src) const {*(unsigned char*)dst = *(unsigned char*)src;}
};

// Factory等々のための先行宣言
class StateBase;
typedef boost::shared_ptr<StateBase> StatePtr;
template<typename Tseq> class SeqState;

// Factoryクラス
struct StateFactoryBase {
    virtual StatePtr create(const RTMEUS::StateProfile &porf) = 0;
    virtual StatePtr create(coil::Properties &prop) = 0;
};
typedef boost::shared_ptr<StateFactoryBase> StateFactoryPtr;
template<typename T>
struct SeqStateFactory : public StateFactoryBase {
    virtual StatePtr create(const RTMEUS::StateProfile &prof) {return StatePtr(new SeqState<T>(prof));}
    virtual StatePtr create(coil::Properties &prop) {return StatePtr(new SeqState<T>(prop));}
};

/*!
 * @breif Stateを管理する基底クラス
 */
class StateBase
{
public:
    StateBase(const RTMEUS::StateProfile &prof) : m_profile(prof){ m_dataProperties.resize(m_profile.length);}
    StateBase(coil::Properties &prop);
    virtual ~StateBase() {}
    const RTMEUS::StateProfile &getStateProfile() const {return m_profile;}
    std::string name() const {return std::string(m_profile.name);}
    virtual void setDefault() = 0;
    virtual void* getPtr(unsigned long index) = 0;
    virtual RTMEUS::DataProfileList getDataProfileList() const {
        return m_profile.data_profiles;
    }
    virtual RTMEUS::DataProfile getDataProfile(const std::string &dataname) const {
        for (unsigned int i=0;i<m_profile.data_profiles.length();i++)
        {
            if (dataname == std::string(m_profile.data_profiles[i].unique_name)) return m_profile.data_profiles[i];
        }
        throw EInvalidStateDataname(dataname); // 見つからなかった
    }
    virtual unsigned int getLength() const = 0;
    virtual StatePtr copy(bool copy_profile = false) const = 0;
    virtual const coil::Properties& getProperties(unsigned int index) const = 0;
    virtual void setAny(CORBA::Any &any) const = 0; // CORBA::Any(から/への)キャスト
    virtual CORBA::Boolean getAny(const CORBA::Any &any) = 0;
    static StatePtr create(const RTMEUS::StateProfile &prof){
        StateFactoryPtr factory = getStateFactory(prof.type);
        return factory->create(prof);
    }
    static StatePtr create(coil::Properties &prop){
        StateFactoryPtr factory = getStateFactory(getStateType(prop["type"]));
        return factory->create(prop);
    }
    
protected:
    StateBase() {} // これはprotected
    static StateFactoryPtr getStateFactory(RTMEUS::StateType type);
    static RTMEUS::StateType getStateType(const std::string &name);
    RTMEUS::StateProfile m_profile;
    std::vector<coil::Properties> m_dataProperties;    
private:    
};

/*!
 * @breif データに配列を持つStateのクラス
 */
template<typename Tseq>
class SeqState : public StateBase
{
public:
    SeqState(const RTMEUS::StateProfile &prof) : StateBase(prof) {
        init();
    }
    SeqState(coil::Properties &prop) : StateBase(prop) {
        m_profile.type =  getStateType(prop["type"]);
        init();
    }
    Tseq& data() {return m_seqdata;}

    virtual ~SeqState() {};
    // デフォルト値をセットする
    virtual void setDefault() {
        for(CORBA::ULong i(0); i < m_profile.length; i++) {
            coil::Properties dataprops = m_dataProperties[i];
            std::stringstream ss(std::string(dataprops["value"].c_str()));
                                             
            if (m_profile.type == RTMEUS::CHARSEQ_TYPE) {
                std::string tmp;
                ss >> tmp;
                m_seqdata[i] = atoi(tmp.c_str());
            } else {
                ss >> m_seqdata[i];
            }
        }
    };
    // 生のデータ先頭ポインタを得る
    virtual void* getPtr(unsigned long index) {return (void*)&(m_seqdata[index]);}
    // サイズを得る
    virtual unsigned int getLength() const {return m_seqdata.length();}
    //
    virtual const coil::Properties& getProperties(unsigned int index) const {return m_dataProperties[index];}
    //
    virtual StatePtr copy (bool copy_profile = false) const {
        SeqState<Tseq> *state = new SeqState<Tseq>();
        // 単に中身をコピーしたいだけならprofileはコピーしない(オーバーヘッド軽減のため)
        if (copy_profile) state->m_profile = m_profile;
        state->m_seqdata.length(m_seqdata.length());
        memcpy(&(state->m_seqdata)[0], &(m_seqdata)[0], m_seqdata.length() * sizeof(m_seqdata[0]));
        return StatePtr(state);
    }
    virtual void setAny(CORBA::Any &any) const {
        any <<= m_seqdata;
    }
    virtual CORBA::Boolean getAny(const CORBA::Any &any){
        Tseq *tmp;
        bool ret = (any >>= tmp);
        if (ret) {
            CORBA::ULong size = std::min(tmp->length(), m_seqdata.length());
            memcpy(&m_seqdata[0], &(*tmp)[0], size * sizeof(m_seqdata[0]));
        }
        return ret;
    }
protected:
    SeqState() {} // これはprotected
    // データ
    Tseq m_seqdata;
private:
    void init(){
        m_seqdata.length(m_profile.length);
        setDefault();
    }
};


void operator <<= (CORBA::Any &any, const StatePtr &state);
CORBA::Boolean operator >>= (const CORBA::Any &any, StatePtr &state);

#endif //STATE_H
